/**
 * @author Fayçal INAJJARANE
 */
package com.appspot.finajjarane.framework.generic;

import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;


@Repository
public class GenericDaoImpl<T, PK> implements IGenericDao<T, PK> {

	// Setup loggin object
	public static final Logger log = Logger.getLogger(GenericDaoImpl.class.getName());


	@PersistenceContext
	private EntityManager em;

	// Grab used entity name
	private Class<T> entityT;


	@SuppressWarnings("unchecked")
	public GenericDaoImpl(){
		// get metadata from generics to initialize the parametrized entity (T Object)
		if(this.getClass().getGenericSuperclass() instanceof ParameterizedType){
			ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
			entityT = (Class<T>) parameterizedType.getActualTypeArguments()[0];
		}
	}


	/**
	 * Add an entity
	 * @param entity
	 * @return True if entity added, False otherwise
	 */
	@Override
	public boolean add(final T entity)  throws Exception{
		log.info("Starting the 'add()' method of " + entity.getClass().getName() + " [GenericDaoImpl]");
		em = EMFactory.get();
		EntityTransaction tx = this.em.getTransaction();
		try{
			tx.begin();
			this.em.persist(entity);

			tx.commit();
			return true;
		}
		catch(Exception e){
			if(tx.isActive()){
				tx.rollback();
			}
			log.severe("Severe error on DAO [EntityType add(" + entity.getClass().getName() + ")] : \n\t" + e.getMessage() );
			return false;
		}
		finally{
			em.close();
			log.info("Ending the 'add()' method of " + entity.getClass().getName() + " [GenericDaoImpl]");
		}
	}

	/**
	 * Update an entity
	 * @param entity
	 * @return the added entity, or null otherwise
	 */
	@Override
	public T merge(final T entity) throws Exception {
		log.info("Starting the 'merge()' method of " + entity.getClass().getName() + " [GenericDaoImpl]");

		em = EMFactory.get();
		EntityTransaction tx = this.em.getTransaction();
		try{
			tx.begin();
			this.em.persist(entity);

			tx.commit();
			return entity;
		}
		catch(Exception e){
			if(tx.isActive()){
				tx.rollback();
			}
			log.severe("Severe error on DAO [EntityType merge(" + entity.getClass().getName() + ")] : \n\t" + e.getMessage() );
			throw e;
		}
		finally{
			em.close();
			log.info("Ending the 'merge()' method of " + entity.getClass().getName() + " [GenericDaoImpl]");
		}
	}


	/**
	 * Find an entity by its key
	 * @param key
	 * @return founded entity, null otherwise
	 */
	@Override
	public T findByKey(final PK key) throws Exception {
		log.info("Starting the 'findByKey(" + key.getClass().getSimpleName() + ")' method of  [GenericDaoImpl]");

		em = EMFactory.get();
		T result;

		EntityTransaction tx = this.em.getTransaction();
		try{
			tx.begin();
			result = this.em.find(this.entityT,key);
			tx.commit();
			return result;
		}
		catch(Exception e){
			if(tx.isActive()){
				tx.rollback();
			}
			log.severe("Severe error on DAO [findByKey()] : " + e.getMessage() );
			throw e;
		}
		finally{
			em.close();
			log.info("Ending the 'findByKey(" + key.getClass().getSimpleName() + ")' method of  [GenericDaoImpl]");
		}
	}


	/**
	 * Get the full list of entities
	 * @return list of all founded entities
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<T> findAll() throws Exception{
		log.info("Starting the 'findByKey()' method of  [GenericDaoImpl]");

		List<T> resultsList;
		em = EMFactory.get();
		EntityTransaction tx = this.em.getTransaction();
		try{
			tx.begin();
			resultsList = this.em.createQuery("select e from " + this.entityT.getClass().getSimpleName() + " e").getResultList();
			tx.commit();
			return resultsList;
		}
		catch(Exception e){
			if(tx.isActive()){
				tx.rollback();
			}
			log.severe("Severe error on DAO [findAll()] : " + e.getMessage() );
			throw e;
		}
		finally{
			em.close();
			log.info("Ending the 'findAll()' method of  [GenericDaoImpl]");
		}
	}


	/**
	 * Get limited list of entities (useful for pagination)
	 * @param page (the number of page in service is decreased by 1 in DAO)
	 * @param maxInPage
	 * @return list of all founded entities, but in limited number
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<T> getLimitedList(int page, final int maxInPage, final String lang, final String orderByField, final String orderByType) throws Exception {

		em = EMFactory.get();

		page = Math.max(page, 1);

		log.info("Starting the 'getLimitedList(" + String.valueOf(page) + "," + String.valueOf(maxInPage) + ")' method of  [GenericDaoImpl]");

		List<T> resultsList;
		String orderbyExpression = "";
		String orderByExpressionType = "";
		String expressionWhereLang = "";

		if(null!=lang){
			expressionWhereLang = " WHERE e.lang='" + lang +"' ";
		}

		if(null==orderByType || orderByType.isEmpty()){
			orderByExpressionType = "ASC";
		}
		else{
			orderByExpressionType = orderByType;
		}

		if(null!=orderByField && !orderByField.isEmpty()){
			orderbyExpression = "ORDER BY e." + orderByField + " " + orderByExpressionType;
		}

		try{

			String query 	= "SELECT e FROM " + this.entityT.getSimpleName() + " e "
																			  + expressionWhereLang
																			  + orderbyExpression;

			resultsList 	= this.em.createQuery(query).setFirstResult((page-1) * maxInPage)
											   			.setMaxResults(maxInPage)
											   			.getResultList();


			return resultsList;

		}
		catch(Exception e){
			log.severe("Severe error on DAO [getLimitedList(" + String.valueOf(page) + "," + String.valueOf(maxInPage) + ")] : " + e.getMessage() );
			throw e;
		}
		finally{
			log.info("Ending the 'getLimitedList(" + String.valueOf(page) + "," + String.valueOf(maxInPage) + ")' method of  [GenericDaoImpl]");
		}
	}

	/**
	 * Remove an entity
	 * @param Key
	 * @return True if entity removed, False otherwise
	 */
	@Override
	public boolean removeByKey(final PK key) throws Exception {

		log.info("Starting the 'removeByKey(" + key.getClass().getSimpleName() + ")' method of  [GenericDaoImpl]");

		em = EMFactory.get();
		EntityTransaction tx = this.em.getTransaction();
		try{
			tx.begin();
			this.em.remove(this.em.find(this.entityT, key));
			tx.commit();
			return true;
		}
		catch(Exception e){
			if(tx.isActive()){
				tx.rollback();
			}
			log.severe("Severe error on DAO [removeByKey(" + key.getClass().getSimpleName() + ")] : " + e.getMessage() );
			throw e;
		}
		finally{
			em.close();
			log.info("Ending the 'removeByKey(" + key.getClass().getSimpleName() + ")' method of  [GenericDaoImpl]");
		}

	}

	/**
	 * Get total entries count for the current entity
	 * @param field
	 * @param lang
	 * @return int
	 */
	@Override
	public int getEntriesCount(final String field, final String lang) throws Exception {

		log.info("Starting the 'getEntriesCount()' method of " + this.entityT.getClass().getSimpleName() + "  [GenericDaoImpl]");

		em = EMFactory.get();
		String expressionWhereLang = "";

		if(null!=lang){
			expressionWhereLang = " WHERE e.lang='" + lang +"' ";
		}

		String query = "SELECT count(:field) FROM " + this.entityT.getSimpleName() + " e " + expressionWhereLang;

		try {
			Integer result = (Integer) em.createQuery(query)
							   			 .setParameter("field", field)
							   			 .getSingleResult();

			return result.intValue();
		}
		catch (Exception e) {
			log.severe("Severe error on DAO [getEntriesCount() of " + this.entityT.getClass().getSimpleName() + "] : " + e.getMessage());
			throw e;
		}
		finally{
			log.info("Ending the 'getEntriesCount()' method of " + this.entityT.getClass().getSimpleName() + "  [GenericDaoImpl]");
		}

	}

}
